{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**To view the examples in this notebook as intended, you need both R and Python language servers up and running.**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Introduction"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Magics support is implemented with kernel-specific definitions. The idea is to:\n",
    "- extract the code inside of magics to another virtual document if they contain foreign code (e.g. code in another language or code which is a standalone script), or\n",
    "- mask the existence of other magics from the LSP server so that it does not complain of invalid syntax\n",
    "\n",
    "By default, a small number of rules for extractors and overrides is provided, showcased below on the example of IPython kernel and rpy2 integration. The user will be able to specify custom overrides and extractors in the settings.\n",
    "\n",
    "#### Technical notes\n",
    "\n",
    "The extraction is performed by implementations of `IForeignCodeExtractor` extractors, with `RegExpForeignCodeExtractor` provided as a simple regular expression-based implementation for the default rules. These rules and this extractor are not designed to be comprehensive, but rather to provide a nice initial experience to the majority of users. More advanced use-cases will require a custom implementation of the `IForeignCodeExtractor` interface.\n",
    "\n",
    "With custom implementation of extractors it is easily possible to extract HTML code from within Python (or any other language) strings or CSS from within HTML for analysis by the LSP server, thus the implementation of extractors is not limited for the use with cell or line magics.\n",
    "\n",
    "The overrides are implemented with regular expressions defined in objects obeying `IMagicOverride` interface.\n",
    "\n",
    "In the future the \"included batteries\" may be moved out to separate extensions i.e. *jupyterlab-lsp-ipython* for default IPython magics support and *jupyterlab-lsp-rpy2* for rpy2 support."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Magics support for IPython kernel"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Shell assignments"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Shell assignments are masked by default so that no \"Invalid syntax\" message is shown (regardless of the linter used):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "!ls"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### `%%python` magic"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from statistics import mean"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x = mean([1, 2, 3])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The content of script magics with aliases defining their language is moved to separate virtual document for LSP linting, completion and all other features; this is then transparently mapped to the top level virtual document and then back to the notebook:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%python\n",
    "# x is not defined in this scope, should be underlined\n",
    "x\n",
    "y = 1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# y is not defined in this scope\n",
    "y"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Consecutive calls to the `%%python` magic will result in separate namespaces. In the extension codebase this is referred as the magic being *standalone*."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%python\n",
    "# y is not defined here (separate namespace from the fist %%python magic)\n",
    "y"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Following IPython magics are treated as standalone:\n",
    "- `%%python`, `%%python2`, `%%python3`, `%%pypy` - use *python* server if available\n",
    "- `%%perl` - uses *perl* server if available\n",
    "- `%%ruby` - uses *ruby* server if available\n",
    "- `%%html --isolated` - uses *html* server if available\n",
    "- `%%sh` - uses *sh* server if available\n",
    "\n",
    "Following IPython magics are treated as parts of a continious virtual document (not standalone):\n",
    "- `%%js`, `%%javascript` - use *javascript* server if available\n",
    "- `%%html` (when without `--isolated` switch) - uses *html* server if available\n",
    "- `%%latex` - uses *latex* server if available\n",
    "- `%%markdown` - uses *markdown* server if available"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### `%%timeit` and all other built-in cell magics"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`%%timeit` reads from the global namespace but it does not write to it:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%timeit\n",
    "# note: x is accessible from global namespace and hence not underlied her:\n",
    "y = x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "y"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Contrary to the `%%python` magic, the `%%timeit` is masked out for the LSP server rather than moved to a separate virtual document. This means that the above example seems to work perfectly only because no linting is active in `%%timeit` magic:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%timeit\n",
    "this_is_undefined_but_wont_be_highlighted"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The user can change this behaviour in settings."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Line and cell magic names can be presented to the LSP server as function calls"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "By default the name of the magic, will be kept and presented in the LSP document, to prevent a false positive of \"defined but unused\" on magic imports or magic function/class definitions. For example:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from jupyter_helpers.selective_import import skip_on_import\n",
    "from jupyter_helpers.selective_import import skip_all_below\n",
    "\n",
    "%skip_all_below"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`%skip_all_below` was transformed into `skip_all_below()` with the built-in regular expression, thus preventing the LSP linter from wrongly falgging the import as unused."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "All of this will be made customizable to the user by the modification of regular expressions set in the settings."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Line and cell magics are not chekced"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We *could* restrict the matches of the default override to only known IPython magics in the potential future jupyterlab-lsp-ipython client extension, allowing the user to disable this feature, but as for now the names are not validated in any way."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## rpy2 intergation"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Both cell and line magics are linted:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# notice the undeline after 1 (\"Trailing whitespaces is superfluous\")\n",
    "%R test = 1     "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%R\n",
    "# again, some trailing spaces added for visibility\n",
    "test2 = data.frame()    \n",
    "x = test\n",
    "x = t"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%R\n",
    "# try <tab> after labell - it should complete to ggplot2::labeller,\n",
    "# and then, offer you a signature suggestion upon '(' insertion.\n",
    "ggplot2::labell"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Line magics and the \"host\" (here Python code) can be interspersed within a single cell:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "undefined_python_variable\n",
    "%R invalid R syntax\n",
    "valid_python = 'syntax'\n",
    "%R valid_r = c('syntax')"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
